const isArrowFunction = fn => {
    const str = fn.toString()
    if (str.match(/{[\s\S]*}/)) {
        return str.replace(/{[\s\S]*}/, "").includes("=>")
    } else {
        return true
    }
}

class EventBus {
    constructor() {
        this.stores = this.stores || {};
    }
    $on(event, fn, ctx) {
        if (typeof fn != "function") {
            console.error('fn must be a function')
            return
        }
        ctx && fn.bind(ctx);
        this.stores[event] ? this.stores[event].push(fn) : this.stores[event] = [fn];
    }

    $emit(event, data) {
        if (this.stores[event]) {
            this.stores[event].forEach(fn => {
                fn && fn(data);
            });
        }
    }

    $once(event, fn, ctx) {
        let _this = this;
        function handler(args) {
            if (!isArrowFunction(fn) && !!ctx) {
                fn.apply(ctx, [args])
            }else{
                fn(args)
            }
            _this.$off(event)
        }
        this.$on(event, handler)
    }

    $off(event, fn) {
        if (!arguments.length) {
            this.stores = {};
            return;
        }

        if (!this.stores[event]) return;

        if (arguments.length === 1) {
            delete this.stores[event];
            return;
        }

        for (let i = 0; i < this.stores[event].length; i++) {
            let cb = this.stores[event][i];
            if (cb === fn) {
                this.stores[event].splice(i, 1);
                break;
            }
        }
    }
}

export default new EventBus()